#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/epoll.h>
#include<sys/time.h>
#include<signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

typedef struct DATA {
    /* data */
    unsigned char  head_type;
    unsigned short len;
    unsigned short file_L;
    unsigned short file_H;
    unsigned char  data[0];
}__attribute__ ((__packed__)) DATA;

struct Server{
    int socket_fd;
    int epoll_fd;
    socklen_t ser_len;
    socklen_t cli_len;
    struct sockaddr_in ser_addr;
    struct sockaddr_in cli_addr;
}srv_data;

void free_soure(int signum)
{
    close(srv_data.socket_fd);
    close(srv_data.epoll_fd);
    exit(signum);
}

int main(int argc,char **argv){
    if (argc < 4) {
        printf("args is not enough!!!\n" \
               "this command is {./file_server [ip] [port] [path/file]}\n"
        );
        return -1;
    }
    int epoll_cnts;
    struct epoll_event event;
    struct epoll_event *ep_events;
    int optlen, option;
    unsigned char read_buf[1200] = {'\0'};

    srv_data.socket_fd = socket(PF_INET,SOCK_STREAM,0);
    if (srv_data.socket_fd < 0) {
        perror("socket");
        return -1;
    }

    signal(SIGINT, free_soure);
    optlen = sizeof(option);
    option = 1;
    if (setsockopt(srv_data.socket_fd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, (void *)&option, optlen) < 0) {
        perror("setsockopt");
        exit(-1);
    }

    memset(&srv_data.ser_addr, 0, sizeof(srv_data.ser_addr));
    srv_data.ser_addr.sin_family      = PF_INET;
    srv_data.ser_addr.sin_addr.s_addr = inet_addr(argv[1]);
    srv_data.ser_addr.sin_port        = htons(atoi(argv[2]));
    srv_data.ser_len                  = sizeof(srv_data.ser_addr);

    if (bind(srv_data.socket_fd, (struct sockaddr *)&srv_data.ser_addr, srv_data.ser_len) < 0) {
        perror("bind");
        return -1;
    }

    if (listen(srv_data.socket_fd,5) < 0) {
        perror("bind");
        return -1;
    }

    srv_data.epoll_fd = epoll_create(1);
    if (srv_data.epoll_fd < 0) {
        perror("epoll_create");
        return -1;
    }

    event.events  = EPOLLIN;
    event.data.fd = srv_data.socket_fd;
    
    if (epoll_ctl(srv_data.epoll_fd, EPOLL_CTL_ADD, srv_data.socket_fd, &event) < 0 ) {
        perror("epoll_ctl");
        return -1;
    }
    ep_events = (struct epoll_event *)malloc(sizeof(struct epoll_event)*50);
    int offset, recv_len, file_fd = -1, open_times = 0, times = 0;;
    while(1){
        epoll_cnts = epoll_wait(srv_data.epoll_fd, ep_events, 50, -1);
        if (epoll_cnts < 0) {
            perror("epoll_wait");
        }
        for (int i = 0; i < epoll_cnts; i++) {
            if (ep_events[i].data.fd == srv_data.socket_fd) {
                if (file_fd >= 0) close(file_fd);
                open_times = 1;
                offset     = 0;
                int apt_fd;
                memset(&srv_data.cli_addr, 0, sizeof(srv_data.cli_addr));
                srv_data.cli_len = sizeof(srv_data.cli_addr);
                apt_fd = accept(srv_data.socket_fd,(struct sockaddr *)&srv_data.cli_addr, &srv_data.cli_len);
                if (apt_fd < 0){ 
                    perror("accept");
                     return -1;     
                }
                fcntl(apt_fd, F_SETFL, O_NONBLOCK);
                event.events  = EPOLLIN;
                event.data.fd = apt_fd;
                if (epoll_ctl(srv_data.epoll_fd, EPOLL_CTL_ADD, apt_fd, &event) < 0) {
                    perror("apt_fd epoll_ctl");
                    return -1;
                }
                file_fd = open(argv[argc - 1], O_RDWR | O_APPEND | O_CREAT, 0666);
                times = 0;
            } else {
                memset(read_buf, '\0', 1200);
                int read_len = read(ep_events[i].data.fd, read_buf, 1024 + sizeof(DATA) + 1);
                offset += read_len - sizeof(DATA) - 1;
                if (read_len == 0) {
                    epoll_ctl(srv_data.epoll_fd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
                    close(ep_events[i].data.fd);
                    break;
                }
                unsigned char buf[1024 + sizeof(DATA) + 1] = {'\0'};
                memcpy(buf, read_buf, 1024 + sizeof(DATA) + 1);
                DATA *recv_msg = (DATA *)buf;
                printf("==%x %x %x %x %x %x %x==\n", read_buf[0], read_buf[1], read_buf[2], read_buf[3], read_buf[4], read_buf[5], read_buf[6]);
                // if (recv_msg->head_type != 0x7b) {
                //     getchar();
                //     printf("==%x %x %x %x %x %x %x==\n", read_buf[0], read_buf[1], read_buf[2], read_buf[3], read_buf[4], read_buf[5], read_buf[6]);
                // }
                int size = write(file_fd, buf, read_len - sizeof(DATA) - 1);
                #ifdef DEBUG
                for (int j = 0; j < read_len - sizeof(DATA) - 1; j++) {
                    printf("%x ", read_buf[j + sizeof(DATA)]);
                }
                printf("\n");
                #endif
                printf("head_type:%x len:%d file_size:%d\n", recv_msg->head_type, recv_msg->len, (recv_msg->file_H << 16 | recv_msg->file_L));
                // times++;
                // printf("===%d %u %u %d %d=== \n",(read_len - sizeof(DATA) - 1), offset, recv_msg->file_size, times, size);//传输bin文件失败
                if (offset == (recv_msg->file_H << 16 | recv_msg->file_L)) {
                    printf("receive file sucessfully\n");
                }
            }
        }
    }
    close(srv_data.socket_fd);
    close(srv_data.epoll_fd);
    return 0;
}